/*
 * PingIP.cpp
 *
 *  Created on: 2015-3-4
 *      Author: root
 */
//#include"Tool.h"
#include"WorkPingApi.h"
#include"WorkPriApi.h"
#ifndef NOUSE_PUBLIC_LOG
#include"PublicLog.h"
#endif
#if USE_PUBLIC_PING
#include"PublicNetHead.h"
#define SHOW_ICMP_INFO 0  //显示日志
#define SHOW_ICMP6_INFO 0  //显示日志
static u_short PING_PID=0;
/*
typedef struct _PINGDATAINFO{
struct timeval sendtime;//发送时间
sequn   seqinfo;
char    ip[40];//发送IP
}PINGDATAINFO;
*/
#define PINGDATAINFO IPPINGCHECK
void InitPingPID(){
    static int bInit=0;
    if(bInit==0){
	    PING_PID=(u_short)(getpid()&0xFFFF);
	    INFO("InitPID:%d PINGDATASIZE:%zd icmphdr:%zd icmp6_hdr:%zd",
			PING_PID,sizeof(PINGDATAINFO),
			sizeof(struct icmphdr),sizeof(struct icmp6_hdr));
	    bInit=1;
    }
}

//返回包大小
int MakePingPacket(char*Packet,int DataSize,sequn seqinfo,const char*dstip,struct timeval*timestampval){
    struct icmphdr*picmp=(struct icmphdr*)Packet;
    PINGDATAINFO*pdatainfo=(PINGDATAINFO *)(Packet+sizeof(struct icmphdr));
    memset(Packet,0,DataSize);
	if(IsIPV6(dstip)==1){
		picmp->type=ICMP6_ECHO_REQUEST;
	}else{
		picmp->type=ICMP_ECHO;
	}
    picmp->code=0;
    picmp->checksum=0;
    picmp->un.echo.id=PING_PID;
    picmp->un.echo.sequence=seqinfo.seqex;
    pdatainfo->seqinfo=seqinfo;
    //strncpy(pdatainfo->ip,dstip,sizeof(pdatainfo->ip));
	memcpy(pdatainfo->ip,dstip,sizeof(pdatainfo->ip));
    gettimeofday(&pdatainfo->sendtime,NULL);
    memcpy(timestampval,&pdatainfo->sendtime,sizeof(struct timeval));
    picmp->checksum=Check_SUM((u_short*)Packet,DataSize); /*校验算法*/
    return DataSize;
}

//大于0返回耗时,-2包不是本程序的包,-1其他错误,0ttl超时
int ReadPingPacket(char*Packet,int DataSize,struct timeval timestamp,u_char check,sequn*seqinfo,char*dstip,char*fromip){
    struct ip*pip=(struct ip*)Packet;
    if(pip->ip_p!=IPPROTO_ICMP){
#if SHOW_ICMP_INFO
        printf("This is not icmp data ip_v:%u PROTO:%u Size:%d\n",pip->ip_v,pip->ip_p,DataSize);
#endif
        return -2;
    }
    int    iphdrlen=pip->ip_hl<<2;
    struct icmphdr *picmphdr=(struct icmphdr*)(Packet+iphdrlen);
//从哪儿收到的	
    //if(fromip)strcpy(fromip,inet_ntoa(pip->ip_src));
	if(fromip)inet_ntop(AF_INET,&pip->ip_src,fromip,16);
//  printf("Recive:%s TYPE:%d SEQ:%d Check:%hhu Pass\n",inet_ntoa(pip->ip_src),picmphdr->type,picmphdr->un.echo.sequence,check);
    if(picmphdr->type==ICMP_ECHOREPLY){
        if(picmphdr->un.echo.id!=PING_PID){
#if SHOW_ICMP_INFO
			printf("PID Err %hhu %hhu\n",picmphdr->un.echo.id,PING_PID);
#endif
			return -2;
        }
        PINGDATAINFO*pdatainfo=(PINGDATAINFO*)(Packet+iphdrlen+sizeof(struct icmphdr));
        if(pdatainfo->seqinfo.flag.check!=check){
#if SHOW_ICMP_INFO			
			printf("CheckID Err %hhu %hhu\n",pdatainfo->seqinfo.flag.check,check);
#endif			
			return -2;
        }
        int usetime=GetTimerSubCommon(timestamp,pdatainfo->sendtime);
//超过10s直接舍弃
        if(usetime>10000)return -2;
//真正的目标IP		
        strncpy(dstip,pdatainfo->ip,sizeof(pdatainfo->ip));
        seqinfo->seqex=pdatainfo->seqinfo.seqex;
#if SHOW_ICMP_INFO
			printf("Recive:%s TYPE:%u SEQ:%d(%d/%d) USETIME:%d\n",inet_ntoa(pip->ip_src),
            picmphdr->type,seqinfo->seqex,seqinfo->flag.id,seqinfo->flag.check,usetime);
			char realsrcip[40]={0};
			strcpy(realsrcip,inet_ntoa(pip->ip_src));
			if(strcmp(realsrcip,dstip)!=0){
				printf("SendTo:%s Recive:%s\n",dstip,realsrcip);
			}
#endif
        return usetime;
    }else
    if(picmphdr->type==ICMP_TIME_EXCEEDED||picmphdr->type==ICMP_DEST_UNREACH||
       picmphdr->type==ICMP_SOURCE_QUENCH||picmphdr->type==ICMP_REDIRECT||
       picmphdr->type==ICMP_PARAMETERPROB){
       struct ip*iph2 =(struct ip*)(Packet+iphdrlen+sizeof(struct icmphdr));
       int    iphdrlen2=iph2->ip_hl<<2;
//原来的目标IP	   
	   inet_ntop(AF_INET,&iph2->ip_dst,dstip,16);
//判断是不是ICMP包
	   if(iph2->ip_p==IPPROTO_ICMP){
		   //只保证前8字节，数据可能没有
            struct icmphdr*picmphdr2=(struct icmphdr*)(Packet+iphdrlen+sizeof(struct icmphdr)+iphdrlen2);
            if(picmphdr2->un.echo.id!=PING_PID){
#if SHOW_ICMP_INFO
				printf("PID2 Err %hhu %hhu\n",picmphdr2->un.echo.id,PING_PID);
#endif				
				return -2;
			}
			if(picmphdr2->type==ICMP_ECHO){
				seqinfo->seqex=picmphdr2->un.echo.sequence;
				if(seqinfo->flag.check!=check){
#if SHOW_ICMP_INFO
					printf("srcip:%s Check Error %hu\n",dstip,seqinfo->seqex);
#endif
					return -2;
				}
#if SHOW_ICMP_INFO
				printf("Recive:%s RealIP:%s TYPE:%d SEQ:%d SEQ:%d(%hu/%hu) Check:%hhu\n",
                       inet_ntoa(pip->ip_src),dstip,picmphdr->type,picmphdr->un.echo.sequence,
                       seqinfo->seqex,seqinfo->flag.id,seqinfo->flag.check,check);
#endif
//ttl超时返回0.其余返回-1
                if(picmphdr->type==ICMP_TIME_EXCEEDED){
					return 0;
				}else{
					return -1;
                }
			}else{
#if SHOW_ICMP_INFO
				printf("This is not icmpecho %hhu\n",picmphdr2->type);
#endif				
				return -2;
			}
	   }else{
#if SHOW_ICMP_INFO
			char ip1_src[40]={0};
            char ip1_dst[40]={0};
            char ip2_src[40]={0};
            char ip2_dst[40]={0};
            uint16_t port_src=0;
            uint16_t port_dst=0;
            strcpy(ip1_src,inet_ntoa(pip->ip_src));
            strcpy(ip1_dst,inet_ntoa(pip->ip_dst));
            strcpy(ip2_src,inet_ntoa(iph2->ip_src));
            strcpy(ip2_dst,inet_ntoa(iph2->ip_dst));
            if(iph2->ip_p==IPPROTO_TCP){
                struct tcphdr*ptcphdr=(struct tcphdr*)(Packet+iphdrlen+sizeof(struct icmphdr)+iphdrlen2);
                port_src=htons(ptcphdr->source);
                port_dst=htons(ptcphdr->dest);
            }else
            if(iph2->ip_p==IPPROTO_UDP){
                struct udphdr*pudphdr=(struct udphdr*)(Packet+iphdrlen+sizeof(struct icmphdr)+iphdrlen2);
                port_src=htons(pudphdr->source);
                port_dst=htons(pudphdr->dest);
            }
            printf("IP1(%s/%s) ICMP TYPE:%hhu CODE:%hhu IP2(%s:%hu/%s:%hu) PROTO:%hhu Size:%d\n",
                     ip1_src,ip1_dst,picmphdr->type,picmphdr->code,
                     ip2_src,port_src,ip2_dst,port_dst,iph2->ip_p,DataSize);
#endif		   
		   return -2;
	   }
    }else
    if(picmphdr->type==ICMP_ECHO){
       return -2;
    }
    return -2;
}
#include<netinet/ip6.h>
int ReadPingPacket6(char*Packet,int DataSize,struct timeval timestamp,u_char check,sequn*seqinfo,char*dstip,char*fromip){
	struct icmp6_hdr *picmphdr=(struct icmp6_hdr*)Packet;
//进程不对抛弃	
	if(picmphdr->icmp6_type==ICMP6_ECHO_REPLY){
//判断PID是否正常		
		if(picmphdr->icmp6_id!=PING_PID){
#if SHOW_ICMP6_INFO
			WARN("IPV6 PID Err %d %d Pass Type:%d",picmphdr->icmp6_id,PING_PID,picmphdr->icmp6_type);
#endif		
			return -2;
		}
		PINGDATAINFO*pdatainfo=(PINGDATAINFO*)(Packet+sizeof(struct icmp6_hdr));
		if(pdatainfo->seqinfo.flag.check!=check){
#if SHOW_ICMP6_INFO			
			printf("IPV6:%s CheckID Err id:%hu check:%hu %hhu\n",pdatainfo->ip,
				pdatainfo->seqinfo.flag.id,
				pdatainfo->seqinfo.flag.check,check);
#endif			
			return -2;   
        }
        int usetime=GetTimerSubCommon(timestamp,pdatainfo->sendtime);
//超过10s直接舍弃
        if(usetime>10000)return -2;
        strncpy(dstip,pdatainfo->ip,sizeof(pdatainfo->ip));
        seqinfo->seqex=pdatainfo->seqinfo.seqex;
#if SHOW_ICMP6_INFO
		INFO("IPV6:%s Time:%d ms",pdatainfo->ip,usetime);
#endif
		return usetime;
	}else
	if(picmphdr->icmp6_type==ICMP6_TIME_EXCEEDED){
		struct ip6_hdr*ipv6h =(struct ip6_hdr*)(Packet+sizeof(struct icmp6_hdr));
		if(ipv6h->ip6_nxt==IPPROTO_ICMPV6){
			struct icmp6_hdr*picmphdr2=(struct icmp6_hdr*)(Packet+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
//判断PID是否正常		
			if(picmphdr2->icmp6_id!=PING_PID){
#if SHOW_ICMP6_INFO
				INFO("IPV6 PID Err %d %d Pass2 Type:%d",picmphdr2->icmp6_id,PING_PID,picmphdr2->icmp6_type);
#endif		
				return -2;
			}	
//看是不是请求包
			if(picmphdr2->icmp6_type==ICMP6_ECHO_REQUEST){
//检查checkID是否一致			
				seqinfo->seqex=picmphdr2->icmp6_seq;
				if(seqinfo->flag.check!=check){
#if SHOW_ICMP6_INFO
					INFO("IPV6 check Err %d %d Pass2",seqinfo->flag.check,check);
#endif					
					return -2;
				}
				inet_ntop(AF_INET6,&ipv6h->ip6_dst,dstip,40);
				if(picmphdr->icmp6_type==ICMP6_TIME_EXCEEDED){
#if SHOW_ICMP6_INFO
					INFO("ICMP6_TIME_EXCEEDED dstip:%s ttl:%d",dstip,seqinfo->flag.id);
#endif						
					return 0;
				}else{
					return -1;
				}
			}else{
#if SHOW_ICMP6_INFO
				WARN("ICMP6_TIME_EXCEEDED type2:%d",picmphdr2->icmp6_type);
#endif					
			}		
		}else{
#if SHOW_ICMP6_INFO			
		char srcip[40]={0};
		char dstip[40]={0};
		inet_ntop(AF_INET6,&ipv6h->ip6_src,srcip,40);
		inet_ntop(AF_INET6,&ipv6h->ip6_dst,dstip,40);
		INFO("ICMP6_TIME_EXCEEDED srcip:%s dstip:%s PayLen:%d NextType:%d",srcip,dstip,
			htons(ipv6h->ip6_plen),ipv6h->ip6_nxt);
#endif			
		}
	}else{
#if SHOW_ICMP6_INFO	
		INFO("IPV6 Other Type:%u code:%u PID:%u SEQ:%u ICMP6_ECHO_REPLY Pass",picmphdr->icmp6_type,picmphdr->icmp6_code,picmphdr->icmp6_id,picmphdr->icmp6_seq);
#endif		
	}
	return -2;
}

#if USE_PUBLIC_CONNECT
//大于0返回耗时,-2包不是本程序的包,-1其他错误,0ttl超时
int ReadPingUDPPacket(char*Packet,int DataSize,CONNECTKEY*key){
    struct ip*pip=(struct ip*)Packet;
    if(pip->ip_p!=IPPROTO_ICMP){
		return -2;
    }
    int    iphdrlen=pip->ip_hl<<2;
    struct icmphdr*picmphdr=(struct icmphdr*)(Packet+iphdrlen);
#if 1
    if(picmphdr->type==ICMP_ECHOREPLY||picmphdr->type==ICMP_ECHO){
		return -2;
    }
#else
    if(picmphdr->type==ICMP_ECHOREPLY){
		printf("Recive:%s TYPE:%d SEQ:%d \n",inet_ntoa(pip->ip_src),picmphdr->type,picmphdr->un.echo.sequence);
    }else
    if(picmphdr->type==ICMP_ECHO){
		printf("Send:%s TYPE:%d SEQ:%d \n",inet_ntoa(pip->ip_dst),picmphdr->type,picmphdr->un.echo.sequence);
    }
#endif
    if(picmphdr->type==ICMP_TIME_EXCEEDED||picmphdr->type==ICMP_DEST_UNREACH||
		picmphdr->type==ICMP_SOURCE_QUENCH||picmphdr->type==ICMP_REDIRECT||
		picmphdr->type==ICMP_PARAMETERPROB){
		struct ip*iph2 =(struct ip*)(Packet+iphdrlen+sizeof(struct icmphdr));
		int    iphdrlen2=iph2->ip_hl<<2;
		if(iph2->ip_p==IPPROTO_UDP){
#if 0
			char ip1_src[40]={0};
			char ip1_dst[40]={0};
			char ip2_src[40]={0};
			char ip2_dst[40]={0};
			uint16_t port_src=0;
			uint16_t port_dst=0;
			strcpy(ip1_src,inet_ntoa(pip->ip_src));
			strcpy(ip1_dst,inet_ntoa(pip->ip_dst));
			strcpy(ip2_src,inet_ntoa(iph2->ip_src));
			strcpy(ip2_dst,inet_ntoa(iph2->ip_dst));
			struct udphdr*pudphdr=(struct udphdr*)(Packet+iphdrlen+sizeof(struct icmphdr)+iphdrlen2);
			port_src=htons(pudphdr->source);
			port_dst=htons(pudphdr->dest);
			printf("IP1(%s/%s) ICMP TYPE:%hhu CODE:%hhu IP2(%s:%hu/%s:%hu) PROTO:%hhu Size:%d\n",
			ip1_src,ip1_dst,picmphdr->type,picmphdr->code,
			ip2_src,port_src,ip2_dst,port_dst,iph2->ip_p,DataSize);
#else
			struct udphdr*pudphdr=(struct udphdr*)(Packet+iphdrlen+sizeof(struct icmphdr)+iphdrlen2);
			strcpy(key->IP,inet_ntoa(iph2->ip_dst));
			key->Port=htons(pudphdr->dest);
			//printf("IP:%s:%hu 端口不可到达\n",key->IP,key->Port);
			return 1;
#endif
		}else{
			return -2;
        }
    }
    return -2;
}
#endif
#endif
